package cn.xinfei.xdecision.engine.runner.executor.strategy.node;

import cn.xinfei.xdecision.common.model.component.decisiontable.vo.DecisionTablesDetailVo;
import cn.xinfei.xdecision.common.model.component.decisiontable.vo.DecisionTablesVersionVo;
import cn.xinfei.xdecision.common.model.component.decisiontable.vo.DecisionTablesVo;
import cn.xinfei.xdecision.common.model.component.strategyout.StrategyOutput;
import cn.xinfei.xdecision.common.model.enginex.model.EngineNodeModel;
import cn.xinfei.xdecision.common.model.enginex.model.NodeJsonModel;
import cn.xinfei.xdecision.common.model.enginex.po.EngineNode;
import cn.xinfei.xdecision.common.service.component.decisiontable.DecisionTablesDetailService;
import cn.xinfei.xdecision.common.service.component.decisiontable.DecisionTablesService;
import cn.xinfei.xdecision.common.service.component.decisiontable.DecisionTablesVersionService;
import cn.xinfei.xdecision.common.service.datax.datamanage.FieldService;
import cn.xinfei.xdecision.common.utils.enums.enginex.ConditionValueTypeEnum;
import cn.xinfei.xdecision.common.utils.enums.enginex.NodeTypeEnum;
import cn.xinfei.xdecision.common.vo.FieldInfoVo;
import cn.xinfei.xdecision.engine.runner.context.XDecisionContext;
import cn.xinfei.xdecision.engine.runner.executor.NodeHandler;
import cn.xinfei.xdecision.engine.runner.executor.XRunner;
import cn.xinfei.xdecision.engine.runner.executor.handler.DecisionTableNodeHandler;
import cn.xinfei.xdecision.engine.runner.executor.model.StateTransmitModel;
import cn.xinfei.xdecision.engine.runner.executor.strategy.EngineNodeRunner;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static cn.xinfei.xdecision.constant.EngineResultEnum.EXCEPTION_NODE_NULL_END;

@XRunner
@Slf4j
@Service
public class DecisionTables extends EngineNodeRunner {

    @Resource
    private DecisionTableNodeHandler decisionTableNodeHandler;

    @Resource
    private DecisionTablesService decisionTablesService;

    @Autowired
    private DecisionTablesDetailService detailService;

    @Autowired
    private FieldService fieldService;

    @Autowired
    private DecisionTablesVersionService decisionTablesVersionService;

    private static final String DECISION_TABLE_CACHE_PRE = "dc_tb_cache_pre_";

    @Override
    public String getServiceName() {
        return EngineNodeRunner.PRE + NodeTypeEnum.DECISION_TABLES.getType();
    }

    @Override
    public void before(NodeHandler nodeHandler) {
        EngineNode engineNode = nodeHandler.getEngineNode();
        Set<String> fieldCodes = new HashSet<>();
        String nodeNodeJson = engineNode.getNodeJson();
        if (StringUtils.isEmpty(nodeNodeJson)) {
            return;
        }

        NodeJsonModel nodeJsonModel = JSONObject.parseObject(nodeNodeJson, NodeJsonModel.class);

        List<EngineNodeModel> componentList = nodeJsonModel.getComponentList();
        if (CollectionUtils.isEmpty(componentList) == false) {
            componentList.forEach(decisionTable -> {
                Long versionId = decisionTable.getVersionId();
                List<DecisionTablesDetailVo> detailVos = detailService.queryDecisionTablesByVersionId(versionId);
                fieldCodes.addAll(detailVos.stream().map(d -> d.getFieldCode()).collect(Collectors.toSet()));

                DecisionTablesVo decisionTablesVo = decisionTablesService.queryByVersionId(versionId);
                nodeHandler.put(DECISION_TABLE_CACHE_PRE + versionId, decisionTablesVo);
            });
        }
    }

    @Override
    public Map<String, StrategyOutput> getOutputFieldInfo(NodeHandler nodeHandler) {
        EngineNode engineNode = nodeHandler.getEngineNode();
        String nodeCode = engineNode.getNodeCode();
        String engineCode = engineNode.getEngineCode();

        Map<String, StrategyOutput> strategyOutputMap = super.getOutputFieldInfo(nodeHandler);
        try {
            String nodeNodeJson = engineNode.getNodeJson();
            if (StringUtils.isEmpty(nodeNodeJson)) {
                return strategyOutputMap;
            }

            NodeJsonModel nodeJsonModel = JSONObject.parseObject(nodeNodeJson, NodeJsonModel.class);

            List<EngineNodeModel> componentList = nodeJsonModel.getComponentList();
            if (CollectionUtils.isEmpty(componentList) == false) {
                Map<String, FieldInfoVo> fieldInfo = metadataProvider.getFieldInfo();
                componentList.forEach(decisionTable -> {
                    List<DecisionTablesVersionVo> decisionTablesVersionVos = decisionTablesVersionService.queryVersionByTablesCodes(decisionTable.getCode(), decisionTable.getVersionNo());
                    for (DecisionTablesVersionVo decisionTablesVersionVo : decisionTablesVersionVos) {
                        String resultFieldCode = decisionTablesVersionVo.getResultFieldCode();
                        FieldInfoVo value = fieldInfo.get(resultFieldCode);
                        if (value != null) {
                            //固定输出一定会在runode中被赋值
                            StrategyOutput output = new StrategyOutput();
                            output.setFieldCode(resultFieldCode);
                            output.setVarValueType(value.getValueType());
                            output.setValueType(ConditionValueTypeEnum.VARIABLE.getCode());
                            output.setFieldValue(decisionTablesVersionVo.getDefaultValue());
                            output.setStage(value.getStage());
                            output.setFixed(true);
                            strategyOutputMap.put(resultFieldCode, output);
                        } else {
                            log.warn("变量不存在,fieldCode={}", resultFieldCode);
                        }
                    }
                });
            }

        } catch (Exception e) {
            log.error("DecisionTablesNode,getOutputFieldInfo:获取决策表指标异常,error={},engineCode={},nodeCode={}", e.getMessage(), engineCode, nodeCode, e);
        }
        return strategyOutputMap;
    }

    @Override
    public Map<String, FieldInfoVo> getInputFieldInfo(NodeHandler nodeHandler) {
        Map<String, FieldInfoVo> fieldVos = super.getInputFieldInfo(nodeHandler);
        EngineNode engineNode = nodeHandler.getEngineNode();
        String nodeCode = engineNode.getNodeCode();
        String engineCode = engineNode.getEngineCode();
        try {
            Set<String> fieldCodes = new HashSet<>();
            String nodeNodeJson = engineNode.getNodeJson();
            if (StringUtils.isEmpty(nodeNodeJson)) {
                return fieldVos;
            }

            NodeJsonModel nodeJsonModel = JSONObject.parseObject(nodeNodeJson, NodeJsonModel.class);

            List<EngineNodeModel> componentList = nodeJsonModel.getComponentList();
            Map<String, FieldInfoVo> fieldInfo = metadataProvider.getFieldInfo();
            if (CollectionUtils.isEmpty(componentList) == false) {
                componentList.forEach(decisionTable -> {
                    Long versionId = decisionTable.getVersionId();
                    List<DecisionTablesDetailVo> detailVos = detailService.queryDecisionTablesByVersionId(versionId);
                    fieldCodes.addAll(detailVos.stream().map(d -> d.getFieldCode()).collect(Collectors.toSet()));
                });
            }


            fieldCodes.forEach(fieldCode -> {
                FieldInfoVo value = fieldInfo.get(fieldCode);
                if (value != null) {
                    fieldVos.put(fieldCode, value);
                }
            });


        } catch (Exception e) {
            log.error("DecisionTablesNode,getInputFieldInfo:获取决策表指标异常,error={},engineCode={},nodeCode={}", e.getMessage(), engineCode, nodeCode, e);
        }
        return fieldVos;
    }


    @Override
    public StateTransmitModel runNode(NodeHandler nodeHandler) {
        EngineNode engineNode = nodeHandler.getEngineNode();
        Set<Long> versionIdList = new HashSet<>();
        String nodeJson = engineNode.getNodeJson();
        String nextNodeCode = nodeHandler.getEngineNode().getNextNodeCodes();
        if (StringUtils.isEmpty(nodeJson)) {
            log.error("DecisionTables node json is null");
            return XDecisionContext.getContext().getStateContext().setEngineResultEnum(EXCEPTION_NODE_NULL_END);
        }
        String nodeCode = engineNode.getNodeCode();

        NodeJsonModel nodeJsonModel = JSONObject.parseObject(nodeJson, NodeJsonModel.class);
        List<EngineNodeModel> componentList = nodeJsonModel.getComponentList();
        componentList.stream().forEach(decisionTable -> {
            Long versionId = decisionTable.getVersionId();
            versionIdList.add(versionId);
        });
        /**
         * 决策表节点只能选择一个决策表实例
         * versionIdList.size=1
         */
        for (Long versionId : versionIdList) {

            //获取决策表decisionTablesVo :decisionTablesService.queryByVersionId(versionId);
            String key1 = DECISION_TABLE_CACHE_PRE + versionId;
            DecisionTablesVo decisionTablesVo = (DecisionTablesVo) nodeHandler.get(key1);
            if (decisionTablesVo == null) {
                log.warn("缓存数据不存在,key={}", key1);
                continue;
            }
            DecisionTablesVersionVo version = decisionTablesVo.getExecuteVersion();
            //获取存放决策表执行结果的变量
            String resultFieldEn = version.getResultFieldCode();
            //执行决策表
            Object executeResult = decisionTableNodeHandler.executeDecisionTables(version);

//            String key = engineNode.getNodeType() + "_" + engineNode.getId() + "_terminal_result";
            if (null == executeResult) {
                //未命中输出，默认值
                executeResult = version.getDefaultValue();
                log.info("决策表执行结果-默认值，nodeCode={},{}={}", nodeCode, resultFieldEn, executeResult);
            }
            setOutput(nodeHandler, resultFieldEn, executeResult);
            log.info("决策表执行结果，{}={}",  resultFieldEn, executeResult);

        }
        return XDecisionContext.getContext().getStateContext().setNextNodeCode(nextNodeCode);
    }


}
